package com.jinglin.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jinglin.component.EsSearchComponent;
import com.jinglin.component.RedisComponent;
import com.jinglin.entity.config.AppConfig;
import com.jinglin.entity.constants.Constants;
import com.jinglin.entity.dto.SysSettingDto;
import com.jinglin.entity.enums.ResponseCodeEnum;
import com.jinglin.entity.enums.VideoFileTransferResultEnum;
import com.jinglin.entity.enums.VideoFileUpdateTypeEnum;
import com.jinglin.entity.enums.VideoStatusEnum;
import com.jinglin.entity.po.VideoInfo;
import com.jinglin.entity.po.VideoInfoFile;
import com.jinglin.entity.po.VideoInfoFilePost;
import com.jinglin.entity.po.VideoInfoPost;
import com.jinglin.exception.BusinessException;
import com.jinglin.mapper.UserInfoMapper;
import com.jinglin.mapper.VideoInfoFilePostMapper;
import com.jinglin.mapper.VideoInfoPostMapper;
import com.jinglin.service.*;
import com.jinglin.utils.CopyTools;
import com.jinglin.utils.StringTools;

import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.tomcat.util.http.fileupload.FileUtils;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.io.File;
import java.io.IOException;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * <p>
 * 视频信息 服务实现类
 * </p>
 *
 * @author ${author}
 * @since 2025-03-12
 */
@Service
public class VideoInfoPostServiceImpl extends ServiceImpl<VideoInfoPostMapper, VideoInfoPost> implements VideoInfoPostService {
    @Autowired
    private RedisComponent redisComponent;
    @Resource
    @Lazy
    private VideoInfoFilePostService videoInfoFilePostService;
    @Autowired
    private VideoInfoFilePostMapper fileMapper;
    @Autowired
    private VideoInfoService videoInfoService;
    @Autowired
    private VideoInfoFileService videoInfoFileService;
    @Autowired
    private AppConfig appConfig;
    @Autowired
    private UserInfoService userInfoService;
    @Autowired
    private UserInfoMapper userInfoMapper;
/*    @Autowired
    private EsSearchComponent esSearchComponent;*/
    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Value("${exchange.video.transfer}")
    private String exchange;
    @Value("${key.video.transfer}")
    private String routingKey;
    @Override
    @Transactional
    public void saveVideoInfo(VideoInfoPost videoInfoPost, List<VideoInfoFilePost> uploadfilePostList) {
        // 检查上传的文件数量是否超过系统设置的最大允许数量
        if (uploadfilePostList.size() > redisComponent.getSysSettingDto().getVideoPCount()) {
            throw new BusinessException(ResponseCodeEnum.CODE_600);
        }
        // 如果videoId存在，则是编辑：
        String videoId = videoInfoPost.getVideoId();
        if (!StringTools.isEmpty(videoId)) {
            VideoInfoPost videoInfoPostDb = this.getById(videoId);
            if (videoInfoPostDb == null) {
                throw new BusinessException(ResponseCodeEnum.CODE_600);
            }
            // 这个视频信息的转码状态为转码中或者待审核时,不允许编辑，报错
            if (ArrayUtils.contains(new Integer[]{VideoStatusEnum.STATUS0.getStatus(), VideoStatusEnum.STATUS2.getStatus()}, videoInfoPostDb.getStatus())) {
                throw new BusinessException(ResponseCodeEnum.CODE_600);
            }
        }
        List<VideoInfoFilePost> deleteFileList = new ArrayList<>();
        List<VideoInfoFilePost> addFileList = uploadfilePostList;
        Boolean updateFileNameFlag = false;
        // 如果videoId为空或者长度为0，则是新增
        if (StringUtils.isEmpty(videoId)){
            videoId = StringTools.getRandomString(Constants.LENGTH_10);
            videoInfoPost.setVideoId(videoId);
            videoInfoPost.setCreateTime(LocalDateTime.now());
            videoInfoPost.setLastUpdateTime(LocalDateTime.now());
            videoInfoPost.setStatus(VideoStatusEnum.STATUS0.getStatus());
            this.save(videoInfoPost);
        } else  {
            // 修改/编辑
            // 删除视频：提交过来的信息没有这个信息，而数据库有，那么就证明是删除逻辑。
                // 首先找到本条视频信息所属的所有视频文件。
            LambdaQueryWrapper<VideoInfoFilePost> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(VideoInfoFilePost::getUserId,videoInfoPost.getUserId());
            queryWrapper.eq(VideoInfoFilePost::getVideoId,videoId);
            List<VideoInfoFilePost> videoInfoFilePosts = videoInfoFilePostService.list(queryWrapper);
            // 将传过来的视频信息列表转为集合，方便后续比对
            Map<String, VideoInfoFilePost> uploadFileMap = uploadfilePostList.stream().collect(Collectors.toMap(item -> item.getUploadId(), item -> item));
            // 将被删除的视频加入deleteFileList
            for (VideoInfoFilePost fileInfo : videoInfoFilePosts) {
                VideoInfoFilePost updateFile = uploadFileMap.get(fileInfo.getUploadId());
                // 如果为空，证明前端传过来的时候删除了这个视频
                if (updateFile == null){
                    deleteFileList.add(fileInfo);
                    // 在判断视频的名字是否有修改
                } else if (!updateFile.getFileName().equals(fileInfo.getFileName())){
                    // 如果名字有修改
                    updateFileNameFlag = true;
                }

            }
            // fileId为null的filePostInfo才是新增的，如果是原来就有的，肯定会把fileId传过来。
            addFileList = uploadfilePostList.stream().filter(item ->item.getFileId() == null).collect(Collectors.toList());
            videoInfoPost.setLastUpdateTime(LocalDateTime.now());
            Boolean changeFlag = checkVideoInfoPostChange(videoInfoPost);
            if (!addFileList.isEmpty()){
                // 如果有新增视频文件。将稿件信息状态设置为  转码中
                videoInfoPost.setStatus(VideoStatusEnum.STATUS0.getStatus());
            } else if (changeFlag || updateFileNameFlag){
                // 或者有稿件信息的修改,设置为待审核
                videoInfoPost.setStatus(VideoStatusEnum.STATUS2.getStatus());
            }
            // 修改稿件信息，操作稿件表
            this.updateById(videoInfoPost);
        }
        /*
        * ==================================  从这里开始 操作videoInfoFilePost ==================================
        * */
        //如果这个集合不为空，则说明有视频被删除了。我们需要去数据库以及文件服务器上面删除视频。
        if (!deleteFileList.isEmpty()){
            List<String> fileIds = deleteFileList.stream().map(item -> item.getFileId()).collect(Collectors.toList());
            fileMapper.delBatchByFileIds(fileIds,videoInfoPost.getUserId());
            // 不在这里删除文件，我们将这个东西放到消息队列中，让他在后台慢慢地去删
            List<String> delFilePathList = deleteFileList.stream().map(item -> item.getFilePath()).collect(Collectors.toList());
            // 用redis做一个简单的消息队列
            redisComponent.addFile2DelQueue(videoId,delFilePathList);
        }
        // 遍历，将上传的文件列表信息补充完整。然后保存到数据库
        Integer index = 1;
        for (VideoInfoFilePost videoInfoFilePost : uploadfilePostList) {
            videoInfoFilePost.setFileIndex(index++);
            videoInfoFilePost.setVideoId(videoId);
            videoInfoFilePost.setUserId(videoInfoPost.getUserId());
            if (videoInfoFilePost.getFileId() == null){
                // 将状态设置为新增
                videoInfoFilePost.setUpdateType(VideoFileUpdateTypeEnum.UPDATE.getStatus());
                // 设置状态为转码中
                videoInfoFilePost.setTransferResult(VideoFileTransferResultEnum.TRANSFER.getStatus());
            }
        }
        // 将数据写入数据库 （如果是新增，使用雪花算法生成fileId）
        this.videoInfoFilePostService.saveOrUpdateBatch(uploadfilePostList);

        if (!addFileList.isEmpty()){
            // 将新增的文件信息写入redis，让其排队转码
            for (VideoInfoFilePost file : addFileList) {
                file.setUserId(videoInfoPost.getUserId());
                file.setVideoId(videoId);
            }
            // 改用消息队列
            for (VideoInfoFilePost videoInfoFilePost : addFileList) {
                rabbitTemplate.convertAndSend(exchange,routingKey,videoInfoFilePost);
            }

/*
            redisComponent.addFile2TransferQueue(addFileList);
*/
        }


    }

    @Override
    @Transactional
    public void auditVideo(String videoId, Integer status, String reason) {
        LambdaUpdateWrapper<VideoInfoPost> wrapper = new LambdaUpdateWrapper<>();
        // 设置一个条件，必须要状态为待审核的时候才能够进行审核。（防止并发）
        wrapper.eq(VideoInfoPost::getStatus, VideoStatusEnum.STATUS2.getStatus());
        wrapper.eq(VideoInfoPost::getVideoId,videoId);
        wrapper.set(VideoInfoPost::getStatus,status);
        boolean updateResult = this.update(wrapper);
        if (!updateResult){
            throw new BusinessException("审核失败，请稍后重试");
        }
        // 更新post文件信息
        LambdaUpdateWrapper<VideoInfoFilePost> filePostWrapper = new LambdaUpdateWrapper<>();
        filePostWrapper.eq(VideoInfoFilePost::getVideoId,videoId);
        filePostWrapper.set(VideoInfoFilePost::getUpdateType,VideoFileUpdateTypeEnum.NO_UPDATE.getStatus());

        /*
        * 审核不通过
        * */
        if (status.equals(VideoStatusEnum.STATUS4.getStatus())){
            return;
        }
        VideoInfoPost dbInfoPost = this.getById(videoId);
        VideoInfo dbInfo = videoInfoService.getById(videoId);
        if (dbInfo == null){
            // 如果为空，则是第一次审核
            SysSettingDto sysSettingDto = redisComponent.getSysSettingDto();
            // 给用户加硬币
            userInfoMapper.updateCoinCountInfo(dbInfoPost.getUserId(),sysSettingDto.getPostVideoCoinCount());
        }
        // 将post信息更新到正式表
        VideoInfo videoInfo = CopyTools.copy(dbInfoPost, VideoInfo.class);
        videoInfoService.saveOrUpdate(videoInfo);

        // 将file信息更新到正式表：先把正式表原视频全部删掉，然后再添加
          // 先删
        LambdaQueryWrapper<VideoInfoFile> deleteWrapper = new LambdaQueryWrapper<>();
        deleteWrapper.eq(VideoInfoFile::getVideoId,videoId);
        videoInfoFileService.remove(deleteWrapper);
          // 在插入
        LambdaQueryWrapper<VideoInfoFilePost> filePostWrapper2 = new LambdaQueryWrapper<>();
        filePostWrapper2.eq(VideoInfoFilePost::getVideoId,videoId);
        List<VideoInfoFilePost> videoInfoFilePostList = videoInfoFilePostService.list(filePostWrapper2);
        List<VideoInfoFile> videoInfoFiles = CopyTools.copyList(videoInfoFilePostList, VideoInfoFile.class);
        videoInfoFileService.saveOrUpdateBatch(videoInfoFiles);

        // 删除消息队列的视频： 这里我没有看懂,为什么要到这个地方去删
        List<String> videoFileDeleteList = redisComponent.getVideoFileDeleteQueue(videoId);
        if (videoFileDeleteList!=null){
            for (String s : videoFileDeleteList) {
                File file = new File(appConfig.getProjectFolder() + Constants.FILE_FOLDER + s);
                if (file.exists()){
                    try {
                        FileUtils.deleteDirectory(file);
                    } catch (IOException e) {
                        log.error("删除文件失败");
                    }
                }
            }
        }
        redisComponent.cleanDeleteVideoFileQueue(videoId);
        //保存videoinfo和videofileinfo到ES中去

/*        esSearchComponent.saveDoc(videoInfo);*/

    }

    // 这个方法用来检查本次操作是否有其他信息的修改
    private Boolean checkVideoInfoPostChange(VideoInfoPost videoInfoPost){
        // 标题、封面、标签、简介
        VideoInfoPost dbInfo = this.getById(videoInfoPost);
        if (!videoInfoPost.getVideoCover().equals(dbInfo.getVideoId())
                ||!videoInfoPost.getVideoName().equals(dbInfo.getVideoName())
                ||!videoInfoPost.getInteraction().equals(dbInfo.getInteraction())
                ||!videoInfoPost.getTags().equals(dbInfo.getTags())  ){
            return true;
        }
        return false;
    }

}
